﻿using System;
using System.Threading.Tasks;
using BookSleeve;
using RedisDemos.Extensions;

namespace RedisDemos.Scenarios
{
    public class OnlineUsersRem : Scenario
    {
        protected override async Task Perform(RedisConnection conn)
        {
            await Task.WhenAll(RegisterUser(conn, new UserModel { UserName = "tank-1", Password = "not safe pwd", }),
                               RegisterUser(conn, new UserModel { UserName = "mario.rossi", Password = "another not safe pwd", }),
                               RegisterUser(conn, new UserModel { UserName = "mario.bianchi", Password = "kill me", }),
                               RegisterUser(conn, new UserModel { UserName = "foo", Password = "bar", }));

            var now = DateTime.Now;

            await Task.WhenAll(LogInUser(conn, new LogInModel { UserName = "foo", Password = "bar", }, loggedAt: now.AddMinutes(-11)),
                               LogInUser(conn, new LogInModel { UserName = "tank-1", Password = "not safe pwd", }, loggedAt: now.AddMinutes(-7)),
                               LogInUser(conn, new LogInModel { UserName = "mario.rossi", Password = "another not safe pwd", }, loggedAt: now.AddMinutes(-5)),
                               LogInUser(conn, new LogInModel { UserName = "tank-1", Password = "not safe pwd", }, loggedAt: now.AddMinutes(-1)));
           
            await WhoIsOnline(conn, at: now);
        }

        private async Task LogInUser(RedisConnection conn, LogInModel model, DateTime loggedAt)
        {
            // GET key
            var userId = await conn.Strings.Get(Db, String.Format("username:{0}", model.UserName));
            // HGET key field 
            var storedPassword = await conn.Hashes.Get(Db, String.Format("user:{0}", userId.AsString()), "Password");

            if (model.Password != storedPassword.AsString())
                throw new InvalidOperationException("The user name or password provided is incorrect.");

            // SADD key member [member ...] 
            await conn.SortedSets.Add(Db, "username:online", model.UserName, (Double)loggedAt.Ticks);

            // ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count] 
            await conn.SortedSets.RemoveRange(Db, "username:online", -1, (Double)loggedAt.AddMinutes(-10).Ticks);

            "User {0} logged at {1}".LogInfo(model.UserName, loggedAt);
        }

        private async Task WhoIsOnline(RedisConnection conn, DateTime at)
        {
            // ZRANGE key start stop [WITHSCORES] 
            var users = await conn.SortedSets.Range(Db, "username:online", 0, -1);

            "*************************************".LogInfo();
            "Online users (last 10 mins from {0}):".LogInfo(at);
            users.ForEach(u => "{0} since {1}".LogInfo(u.Key.AsString(), new DateTime((Int64)u.Value)));
        }

        private async Task RegisterUser(RedisConnection conn, UserModel model)
        {
            // SADD key member [member ...] 
            var wasAdded = await conn.Sets.Add(Db, "username:all", model.UserName);
            if (wasAdded == false)
                throw new InvalidOperationException("User name already exists.");

            // INCR key 
            var newId = await conn.Strings.Increment(Db, "users:nextid");

            // SET key value
            // HMSET key field value [field value ...] 
            await Task.WhenAll(conn.Strings.Set(Db, String.Format("username:{0}", model.UserName), newId),
                               conn.Hashes.Set(Db, String.Format("user:{0}", newId), model.AsHash()));
        }
    }
}
